package com.sz.biz.logistics.base.service.impl;

import com.sz.biz.common.base.dto.AreaDto;
import com.sz.biz.common.base.dto.CityModel;
import com.sz.biz.common.base.dto.CommonAreaList;
import com.sz.biz.common.base.dto.CountryArea;
import com.sz.biz.common.base.service.AreaService;
import com.sz.biz.common.constants.ErrorCodes;
import com.sz.biz.common.constants.LanguageCode;
import com.sz.biz.common.constants.ModuleNames;
import com.sz.biz.logistics.base.dto.RegionSchemaDto;
import com.sz.biz.logistics.base.entity.Region;
import com.sz.biz.logistics.base.entity.RegionItem;
import com.sz.biz.logistics.base.entity.RegionSchema;
import com.sz.biz.logistics.base.service.*;
import com.sz.biz.logistics.constants.ProductGroupCode;
import com.sz.biz.logistics.core.entity.Product;
import com.sz.biz.logistics.core.entity.ProductGroup;
import com.sz.biz.logistics.core.service.ProductGroupService;
import com.sz.biz.logistics.core.service.ProductService;
import com.sz.biz.logistics.core.service.QuotationService;
import com.sz.common.base.constants.UserActions;
import com.sz.common.base.dao.ParamData;
import com.sz.common.base.dto.QResultDto;
import com.sz.common.base.dto.QueryParams;
import com.sz.common.base.dto.ResultDto;
import com.sz.common.base.exception.Exceptions;
import com.sz.common.base.service.AbstractService;
import com.sz.common.base.utils.CollectionUtils;
import com.sz.common.base.utils.EntityUtils;
import com.sz.common.base.utils.StringUtils;
import com.sz.common.core.service.PrincipalLogUtils;
import com.sz.common.core.service.PrincipalUtils;
import com.sz.common.core.system.entity.User;
import com.sz.common.core.system.entity.UserDetail;
import org.apache.commons.collections.map.HashedMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.sql.Timestamp;
import java.util.*;

/**
 * Package: com.sz.biz.logistics.base.service.impl <br>
 * Function: TODO: ADD FUNCTION <br>
 * Author: hpeng.wang. <br>
 * Date: 2017-02-15 15:00:00.
 */
@Service("RegionSchemaService")
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public class RegionSchemaServiceImpl extends AbstractService implements RegionSchemaService {
    @Autowired
    private RegionService regionService;
    @Autowired
    private AreaService areaService;
    @Autowired
    private RegionItemService regionItemService;

    @Autowired
    private QuotationService quotationService;

    @Override
    protected String getMapperNamespace() {
        return "com.sz.biz.logistics.base.mapper.RegionSchemaMapper";
    }

    @Override
    public String save(RegionSchema entry) {
        //判断分区方案是否有引用
        boolean isRef = quotationService.isRegionSchemaReferenced(entry.getUid(), true);
        if (isRef) {
            throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_REGION_SCHEMA_REF, entry.getUid());
        }

        ParamData pd = new ParamData();
        pd.put("code", entry.getCode());
        int count = (Integer) dao.findForObject(getSqlName("findCountByCode"), pd);
        if (count > 0) {
            throw Exceptions.bizException(ErrorCodes.ERROR_MODEL_CODE_EXIST, "");
        }
        Date now = dao.getDbDate();
        entry.setUid(generateNewUid());
        entry.setActiveTime(now);
        entry.setCreateTime(now);
        entry.setIsActive(true);
        entry.setCreateUserId(PrincipalUtils.getAccountId());
        entry.setLastUpdateUserId(PrincipalUtils.getAccountId());
        dao.save(getSqlName("insertSelective"), entry);
        //保存操作日志
        Map<String, Object> dataBackup = new HashMap<>();
        dataBackup.put("ADD_REGION_SCHEMA", entry);
        PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_REGION_SCHEMA, UserActions.ADD, "添加分区方案", dataBackup);
        return entry.getUid();
    }

    @Override
    public int delete(Integer id) {
        RegionSchema entry = this.findById(id);
        if (entry == null) {
            throw Exceptions.bizException(ErrorCodes.ERROR_OBJECT_EMPTY);
        }

        //判断分区方案是否有引用
        boolean isRef = quotationService.isRegionSchemaReferenced(entry.getUid(), false);
        if (isRef) {
            throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_REGION_SCHEMA_REF, entry.getUid());
        }

        Date now = dao.getDbDate();
        //备份数据
        Map<String, Object> dataBackup = new HashMap<>();
        dataBackup.put("DELETE_REGION_SCHEMA", entry);
        PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_REGION_SCHEMA, UserActions.DELETE, "删除分区方案", dataBackup);
        entry.setIsActive(false);
        entry.setInactiveTime(now);
        entry.setIsDeleted(true);
        //删除分区方案下的所有分区
        ParamData pd = new ParamData();
        pd.put("id", id);
        RegionSchema regionSchema = (RegionSchema) dao.findForObject(getSqlName("findById"), pd);
        List<String> list = new ArrayList<String>();
        list.add(regionSchema.getUid());
        regionService.deleteBySchemaIds(list);
        //删除分区方案
        return dao.update(getSqlName("deleteByUid"), entry);

    }

    @SuppressWarnings("unchecked")
    @Override
    public void batchdelete(List<Integer> ids) {
        List<RegionSchema> list = findByIds(ids);
        Date now = dao.getDbDate();
        if (list.size() > 0) {
            //分区方案UID
            List<String> regionSchemaList = new ArrayList<String>();
            for (RegionSchema entry : list) {
                //判断分区方案是否有引用
                boolean isRef = quotationService.isRegionSchemaReferenced(entry.getUid(), false);
                if (isRef) {
                    throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_REGION_SCHEMA_REF, entry.getUid());
                }

                entry.setIsActive(false);
                entry.setInactiveTime(now);
                entry.setIsDeleted(true);
                ParamData pd = new ParamData();
                pd.put("id", entry.getId());
                RegionSchema regionSchema = (RegionSchema) dao.findForObject(getSqlName("findById"), pd);
                regionSchemaList.add(regionSchema.getUid());

            }
            //先删除分区方案下分区
            regionService.deleteBySchemaIds(regionSchemaList);
            //批量删除分区
            batchDeleteByIds(list);
            batchDeleteByUids(list);
            // 数据备份
            Map<String, Object> dataBackup = new HashedMap();
            dataBackup.put("DELETE_REGION_SCHEMA", list);
            PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_REGION_SCHEMA, UserActions.DELETE,
                    "删除分区方案", dataBackup);
        }
    }

    @Override
    public void batchUpdate(List<RegionSchema> list) {
        list.forEach(regionSchema -> {
            //判断分区方案是否有引用
            boolean isRef = quotationService.isRegionSchemaReferenced(regionSchema.getUid(), true);
            if (isRef) {
                throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_REGION_SCHEMA_REF, regionSchema.getUid());
            }
        });

        dao.batchUpdate(getSqlName("updateByPrimaryKeySelective"), list);
    }

    @Override
    public void batchDeleteByIds(List<RegionSchema> list) {
        list.forEach(regionSchema -> {
            //判断分区方案是否有引用
            boolean isRef = quotationService.isRegionSchemaReferenced(regionSchema.getUid(), true);
            if (isRef) {
                throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_REGION_SCHEMA_REF, regionSchema.getUid());
            }
        });
        dao.batchUpdate(getSqlName("deleteById"), list);
    }

    @Override
    public void batchDeleteByUids(List<RegionSchema> list) {
        list.forEach(regionSchema -> {
            //判断分区方案是否有引用
            boolean isRef = quotationService.isRegionSchemaReferenced(regionSchema.getUid(), false);
            if (isRef) {
                throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_REGION_SCHEMA_REF, regionSchema.getUid());
            }
        });
        dao.batchUpdate(getSqlName("deleteByUid"), list);
    }

    @Override
    public int update(RegionSchema entry) {
        //判断分区方案是否有引用
        boolean isRef = quotationService.isRegionSchemaReferenced(entry.getUid(), true);
        if (isRef) {
            throw Exceptions.bizException(ErrorCodes.ERROR_BIZ_REGION_SCHEMA_REF, entry.getUid());
        }
        ParamData pd = new ParamData();
        pd.put("id", entry.getId());
        pd.put("code", entry.getCode());
        int count = (Integer) dao.findForObject(getSqlName("findCountByCode"), pd);
        if (count > 0) {
            throw Exceptions.bizException(ErrorCodes.ERROR_MODEL_CODE_EXIST, "");
        }
        //添加要更新数据的日志信息
        Map<String, Object> dataBackup = new HashMap<>();
        dataBackup.put("DELETE_REGION_SCHEMA", entry);
        PrincipalLogUtils.addOperationLog(ModuleNames.BIZ, ModuleNames.BIZ_REGION_SCHEMA, UserActions.UPDATE, "修改分区方案", dataBackup);
        RegionSchema regionSchema = new RegionSchema();
        EntityUtils.copyPropertiesIgnoreNull(entry, regionSchema);
        RegionSchema rs = new RegionSchema();
        Timestamp timestamp = dao.getDbDate();
        rs.setIsActive(false);
        rs.setLastUpdateUserId(PrincipalUtils.getAccountId());
        rs.setInactiveTime(timestamp);
        rs.setLastUpdateTime(timestamp);
        rs.setId(entry.getId());
        dao.update(getSqlName("updateByPrimaryKeySelective"), rs);

        regionSchema.setId(null);
        regionSchema.setLastUpdateUserId(PrincipalUtils.getAccountId());
        regionSchema.setActiveTime(timestamp);
        regionSchema.setIsActive(true);
        dao.save(getSqlName("insertSelective"), regionSchema);
        return regionSchema.getId();
    }

    /**
     * 更新冗余的itemDetail
     *
     * @param id         更新的id
     * @param itemDetail 更新的itemDetail
     * @return 新增是否成功
     */
    @Override
    public void updateItemDetail(Integer id, String itemDetail) {
        Map<String, Object> map = new HashMap<>();
        map.put("id", id);
        map.put("itemDetail", itemDetail);
        dao.update(getSqlName("updateItemDetail"), map);
    }

    @Override
    public RegionSchema findById(Integer id) {
        ParamData pd = new ParamData();
        pd.put("id", id);
        RegionSchema entry = (RegionSchema) dao.findForObject(getSqlName("findById"), pd);
        entry.setCreator(getUserName(entry.getCreateUserId()));
        entry.setCreatorCode(getUserCode(entry.getCreateUserId()));
        entry.setMender(getUserName(entry.getLastUpdateUserId()));
        return entry;
    }

    /**
     * 根据分区方案的uid查询出当前分区方案下所有分区明细的[国家]和[国家名字]
     *
     * @param uids      分区方案的uid
     * @param q         查询的参数
     * @param pageIndex 起始页
     * @param pageSize  分页大小
     * @return 如果当前当前分区方案为运输工具，那么返回的是运输工具(海运或空运)所在国家的集合
     */
    @Override
    public ResultDto findRegionCountryByUid(List<String> uids, String q, int pageIndex, int pageSize) {
        ResultDto resultDto;
        List<Integer> regionIdList = regionService.findIdBySchemaUids(uids, true);
        resultDto = regionItemService.findByRegionIds(regionIdList, q, pageIndex, pageSize);
        return resultDto;
    }

    /**
     * 根据分区方案的uid查询出当前分区方案下所有的运输工具(海运或空运)的id与name
     *
     * @param uid         分区方案的uid
     * @param countryCode 国家二字码
     * @param cityId
     * @param pageIndex   起始页
     * @param pageSize    分页大小
     * @param productId
     * @param q           查询的参数
     * @return 返回的是运输工具的id与name
     */
    @Override
    public ResultDto findTransportByUid(String uid, String countryCode, String cityId, int pageIndex, int pageSize, Integer productId, String q) {
        ResultDto dto = new ResultDto();
        if (!StringUtils.isEmpty(countryCode)) {
            //查询分区方案的地址类型
            RegionSchema regionSchema = this.findEntryActivedByUid(uid);
            int addressType = regionSchema.getAddressType();
            //取出方案下面的分区id集合
            List<Integer> regionIdList = regionService.findIdBySchemaUid(uid);
            dto = regionItemService.findAirByRegionIds(regionIdList, countryCode, cityId, q, pageIndex, pageSize, addressType);
        }
        return dto;
    }

    /**
     * 根据分区方案、国家、地区id 查询出对应的地区数据，供添加地址使用。
     *
     * @param uids        分区方案的uid
     * @param countryCode 国家二字码
     * @param areaId      地区id
     * @param q           查询的参数
     * @param pageIndex   起始页
     * @param pageSize    分页大小
     * @return 地区数据
     */
    @Override
    public ResultDto findCountryAreaByUid(List<String> uids, String countryCode, String areaId, String q, int pageIndex, int pageSize) {
        ResultDto dto = new ResultDto();
        //1、判断参数
        this.judgeParam(uids, countryCode);
        //2、查询现有分区明细表中所有数据，并选出当前国家的分区详情数据
        List<Integer> regionIds = regionService.findIdBySchemaUids(uids, true);

        //处理国外国家的地址
        if (!LanguageCode.CN_UPPER.equalsIgnoreCase(countryCode)) {
            //如果某个国家在分区item中已经配置了一个type=1的数据，那么代表要查询这个国家的所有数据。所以count为0，代表没有配置，要继续从分区中查询。
            int count = regionItemService.findAreaNullCountByRegionIds(regionIds, countryCode);
            if (count == 0 && !CollectionUtils.isEmpty(regionIds)) {//没有配置type为1的数据。
                dto = regionItemService.findCityModelByRegionIds(regionIds, countryCode, q, pageIndex, pageSize);
            } else {//交给地区表处理
                dto = areaService.findAreaByParentId(countryCode, countryCode, q, pageIndex, pageSize);
            }
            return dto;
        }
        //处理国内地址
        List<CityModel> returnAreaList = new ArrayList<>();
        List<RegionItem> regionItemList = regionItemService.findByRegionIds(regionIds);
        boolean bContains = false;
        if (!CollectionUtils.isEmpty(regionItemList)) {//分区详情中没有数据，那么交给国家表进行处理
            List<RegionItem> regionItems = new ArrayList<>();
            for (RegionItem regionItem : regionItemList) {
                if (regionItem.getType() == CountryAreaType.PORTADDRESS.value() || regionItem.getType() == CountryAreaType.AIRADDRESS.value()) {
                    throw Exceptions.bizException(ErrorCodes.ERROR_COMMON, "分区类型异常，应为全球速运！");
                }
                if (countryCode.equalsIgnoreCase(regionItem.getCountry())) {
                    regionItems.add(regionItem);
                }
            }
            if (CollectionUtils.isEmpty(regionItems)) {
                throw Exceptions.bizException(ErrorCodes.ERROR_COMMON, "未找到指定国家的分区详情数据！");
            }


            //3、提取全路径（国内与国外地区都有）的list集合
            CommonAreaList list = new CommonAreaList(regionItems);
            areaService.fillAllPathCommonAreaNames(list);
            List<CountryArea> dataCountryAreaList = list;

            //4、如果areaId不为空，那么以areaId来从字符串中查找，如果为空，那么代表当前传递过来的数据是国家，找国家的下一级或国外的数据
            String strCountryArea;
            if (!StringUtils.isEmpty(areaId)&& !"0".equals(areaId)) {
                strCountryArea = "/" + areaId + "/";
            } else {
                strCountryArea = countryCode + "/";
            }

            //5、从分区详情的数据里拼接出需要返回的数据


            for (CountryArea countryArea : dataCountryAreaList) {
                if(countryArea.getCountryCode().equalsIgnoreCase(countryCode)){
                    bContains =true;
                }
                if (countryArea.getCountryCode().contains(strCountryArea)) {

                    String[] areaArray = countryArea.getCountryCode().split("/");

                    int index = RegionSchemaServiceImpl.printArray(areaArray, strCountryArea.replaceAll("/", ""));
                    String[] areaNameArray = countryArea.getName().split("/");
                    returnAreaList.add(new CityModel(areaArray[index + 1], areaNameArray[index + 1]));
                }
            }
        }
        //6、如果分区详情中没有返回数据，那么从地区表中查询相应的数据
        if (CollectionUtils.isEmpty(returnAreaList) || bContains) {
            if (countryCode.equalsIgnoreCase(areaId) || areaId == null || "".equals(areaId)) {
                areaId = "0";
            }
            List<AreaDto> cityModels = areaService.findAreaNameByParentId(countryCode, areaId, null, 0, 1000);
            for (AreaDto areaDto : cityModels) {
                returnAreaList.add(new CityModel(areaDto.getId(), areaDto.getName()));
            }
        }

        //7、对数据进行去重
        returnAreaList = CollectionUtils.removeDuplication(returnAreaList);

        dto.setData(returnAreaList);
        //8、返回数据
        return dto;
    }

    private void judgeParam(List<String> uids, String countryCode) {
        if (StringUtils.isEmpty(countryCode)) {
            throw Exceptions.bizException(ErrorCodes.ERROR_COMMON, "国家二字码不能为空！");
        }
        List<RegionSchema> regionSchemas = this.findEntryActivedByUids(uids);
        for (RegionSchema regionSchema : regionSchemas) {
            if (regionSchema.getAddressType() != 11) {
                throw Exceptions.bizException(ErrorCodes.ERROR_COMMON, "分区类型异常，应为全球速运！");
            }
        }
    }

    //遍历数组
    public static int printArray(String[] array, String value) {
        for (int i = 0; i < array.length; i++) {
            if (array[i].equalsIgnoreCase(value)) {
                return i;
            }
        }
        return -1;//当if条件不成立时，默认返回一个负数值-1
    }

    @Override
    public RegionSchemaDto findActivedByUid(String Uid) {
        RegionSchemaDto dto = new RegionSchemaDto();
        RegionSchema entry = findSimpleActivedByUid(Uid);
        EntityUtils.copyPropertiesIgnoreNull(entry, dto);
        dto.setCreator(getUserName(entry.getCreateUserId()));
        dto.setCreatorCode(getUserCode(entry.getCreateUserId()));
        dto.setMender(getUserName(entry.getLastUpdateUserId()));
        List<Region> list = regionService.findRegionBySchemaUids(Arrays.asList(Uid), true, null);
        dto.setRegionlist(list);
        return dto;
    }

    @Override
    public RegionSchema findSimpleActivedByUid(String uid) {
        ParamData pd = new ParamData();
        pd.put("Uid", uid);
        return (RegionSchema) dao.findForObject(getSqlName("findByUId"), pd);
    }
    @Override
    public List<RegionSchema> findEntryActivedByUids(List<String> uids) {
        List<RegionSchema> list = new ArrayList<>();
        if (!CollectionUtils.isEmpty(uids)) {
            ParamData pd = new ParamData();
            pd.put("uids", uids);
            list = dao.findForList(getSqlName("findShortByUids"), pd, RegionSchema.class);
        }
        return list;
    }

    @Override
    public RegionSchema findEntryActivedByUid(String uid) {
        RegionSchema regionSchema = null;
        if (!StringUtils.isEmpty(uid)) {
            List<RegionSchema> list = this.findEntryActivedByUids(Arrays.asList(uid));
            if (!CollectionUtils.isEmpty(list)) {
                regionSchema = list.get(0);
            }
        }
        return regionSchema;
    }

    @Override
    public RegionSchemaDto findByUidAndTime(String Uid, Date time) {
        RegionSchemaDto dto = new RegionSchemaDto();
        ParamData pd = new ParamData();
        pd.put("Uid", Uid);
        pd.put("time", time);
        RegionSchema entry = (RegionSchema) dao.findForObject(getSqlName("findByUIdAndTime"), pd);
        EntityUtils.copyPropertiesIgnoreNull(entry, dto);
        dto.setCreator(getUserName(entry.getCreateUserId()));
        dto.setCreatorCode(getUserCode(entry.getCreateUserId()));
        dto.setMender(getUserName(entry.getLastUpdateUserId()));
        List<Region> list = regionService.findRegionBySchemaUids(Arrays.asList(Uid), null, time);
        dto.setRegionlist(list);
        return dto;
    }

    @Override
    public List<RegionSchema> findByIds(List<Integer> ids) {
        if (ids == null || ids.size() <= 0) {
            throw Exceptions.bizException(ErrorCodes.ERROR_ARGUMENT_EMPTY);
        }
        ParamData pd = new ParamData();
        pd.put("ids", ids);
        List<RegionSchema> list = dao.findForList(getSqlName("findByIds"), pd, RegionSchema.class);
        return list;
    }

    @Override
    public RegionSchema findByParam(String code, String name, Integer id) {
        if (!(StringUtils.hasLength(code) && StringUtils.hasLength(name))) {
            throw Exceptions.bizException(ErrorCodes.ERROR_ARGUMENT_EMPTY, "code/name");
        }
        ParamData pd = new ParamData();
        pd.put("id", id);
        pd.put("code", code);
        pd.put("name", name);
        RegionSchema entry = (RegionSchema) dao.findForObject(getSqlName("findByParam"), pd);
        return entry;
    }

    @Override
    public RegionSchema findByCodeAndType(String code, Integer type) {
        if (!(StringUtils.hasLength(code))) {
            throw Exceptions.bizException(ErrorCodes.ERROR_ARGUMENT_EMPTY, "code");
        }
        ParamData pd = new ParamData();
        pd.put("code", code);
        pd.put("type", type);
        RegionSchema entry = (RegionSchema) dao.findForObject(getSqlName("findByCodeAndType"), pd);
        return entry;
    }

    @Override
    public QResultDto findByName(String name) {
        ParamData pd = new ParamData();
        pd.put("name", name);
        List<Map> list = dao.findForList(getSqlName("findByName"), pd, Map.class);
        return new QResultDto(list, pd.getPagination());
    }

    @Override
    public QResultDto findByParam(QueryParams params) {
        ParamData pd = this.convertQueryParams(params);
        pd.put("language", PrincipalUtils.getLocalLanguage());
        List<RegionSchema> list = dao.findForList(getSqlName("findByParamListPage"), pd, RegionSchema.class);
        for (RegionSchema regionSchema : list) {
            regionSchema.setCreator(getUserName(regionSchema.getCreateUserId()));
            regionSchema.setCreatorCode(getUserCode(regionSchema.getCreateUserId()));
        }
        return new QResultDto(list, pd.getPagination());
    }

    @Override
    public void regionSchemaCheckCode(int sid, String code) {
        ParamData pd = new ParamData();
        pd.put("id", sid);
        pd.put("code", code);
        int count = (Integer) dao.findForObject(getSqlName("findCountByCode"), pd);
        if (count > 0) {
            throw Exceptions.bizException(ErrorCodes.ERROR_MODEL_CODE_EXIST, "");
        }
    }

    /*
     * 获取用户名称
     */
    private String getUserName(int id) {
        ParamData pdUser = new ParamData();
        pdUser.put("userId", id);
        UserDetail userDetail = (UserDetail) dao.findForObject("UserDetailMapper.findByUserId", pdUser);
        return userDetail.getFullName();
    }

    /*
     * 获取用户编码
     */
    private String getUserCode(int id) {
        ParamData pdCode = new ParamData();
        pdCode.put("id", id);
        User user = (User) dao.findForObject("UserMapper.findById", pdCode);
        return user.getCode();
    }
}
